/* Definitions for miscelaneous functions.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#include <chrono>
#include "interp.hpp"
#include "thread.hpp"
#include "xtime.hpp"

KP_DECLS_BEGIN

uint32_t upsize (uint32_t n)
{
  if (n < 8)
    return (8);

  uint32_t ret = n;

  ret |= ret >> 1;
  ret |= ret >> 2;
  ret |= ret >> 4;
  ret |= ret >> 8;
  ret |= ret >> 16;
  return (++ret);
}

bool varobj::set_flag (uint32_t flg)
{
  bool ret;

  if (singlethr_p ())
    {
      ret = (this->vo_full & flg) != flg;
      this->vo_full |= flg;
    }
  else
    ret = (atomic_or ((atomic_t *)&this->vo_full, flg) & flg) != flg;

  return (ret);
}

bool varobj::clr_flag (uint32_t flg)
{
  bool ret;

  if (singlethr_p ())
    {
      ret = (this->vo_full & flg) != 0;
      this->vo_full &= ~flg;
    }
  else
    ret = (atomic_and ((atomic_t *)&this->vo_full, flg) & flg) != 0;

  return (ret);
}

bool varobj::cas_flag (uint32_t mask, uint32_t set)
{
  if (singlethr_p ())
    {
      uintptr_t prev = this->vo_full;
      this->vo_full = (this->vo_full & ~mask) | set;
      return (prev == this->vo_full);
    }

  while (true)
    {
      atomic_t prev = this->vo_full;
      atomic_t nval = (prev & ~mask) | set;

      if (prev == nval)
        return (false);
      else if (atomic_cas_bool ((atomic_t *)&this->vo_full, prev, nval))
        return (true);

      atomic_spin_nop ();
    }
}

template <class Clock>
static double get_tstamp ()
{
  auto now = Clock::now().time_since_epoch ();
  auto ns = std::chrono::duration_cast<std::chrono::nanoseconds> (now);
  return (ns.count () * 1e-9);
}

double monotonic_time ()
{
  return (get_tstamp<std::chrono::steady_clock> ());
}

double real_time ()
{
  return (get_tstamp<std::chrono::system_clock> ());
}

KP_DECLS_END
